package controller;

import java.util.ArrayList;

import command.Attack;
import command.Command;
import command.Attack.Type;

import Player.Enemy;
import Player.PG;
import concept.Attributes;
import gdrObject.Armor;
import gdrObject.GdrObject;
import gdrObject.Weapon;
import utilities.Percentage;
import utilities.ResultEvent;
import utilities.ResultListener;
import utilities.ResultType;

public class CommandManager {
	
	public CommandManager(){
		listeners = new ArrayList<ResultListener>();
		player = LocalManager.getCurrentPlayer();
	}
	
	public void addResultListener(ResultListener listener){
		if(listener != null)
			listeners.add(listener);
	}
	
	public void execCommand(Command c, Enemy enemy){
		switch(c.getType()){
		case ATTACK:
			fireResultEvent(execPlayerAttack((Attack)c, player, enemy));
		case ENEMY_ATTACK:
			fireResultEvent(execEnemyAttack((Attack)c, player, enemy));
			break;
		case RETREAT:
			fireResultEvent(execRetreat(c, player, enemy));
			break;
		case CHANGE:
			fireResultEvent(execChange(c, player));
			break;
		default:
			fireResultEvent(new ResultEvent(CommandManager.class, ResultType.ERROR, 0, "Unsupported Command"));
		}
	}
	
	private void fireResultEvent(final ResultEvent e){
		for(final ResultListener l: listeners){
			new Thread(){
				public void run() {
					l.stateChanged(e);
				}
			}.start();
			
		}
	}
	
	private static ResultEvent execChange(Command c, PG player){
		if(c.getName().equals("pack weapon")){
			player.packAwayEquippedWeapon();
			return new ResultEvent(CommandManager.class, ResultType.WEAPON_CHANGED, 0, null);
		}else if(c.getName().equals("pack armor")){
			player.packAwayEquippedArmor();
			return new ResultEvent(CommandManager.class, ResultType.ARMOR_CHANGED, 0, null);
		}else{
			GdrObject obj = player.getObject(c.getName());
			if(obj != null){
				if(obj instanceof Weapon){
		            try {
		                player.equipWeapon(obj.getName());
		                return new ResultEvent(CommandManager.class, ResultType.WEAPON_CHANGED, 0, null);
		            } catch (Exception ex) {
		            	return new ResultEvent(CommandManager.class, ResultType.ERROR, 0, ex.getMessage());
		            }
		        }
		        else if(obj instanceof Armor){
		            try {
		                player.equipArmor(obj.getName());
		                return new ResultEvent(CommandManager.class, ResultType.ARMOR_CHANGED, 0, null);
		            } catch (Exception ex) {
		            	return new ResultEvent(CommandManager.class, ResultType.ERROR, 0, ex.getMessage());
		            }
		        }else{
					return new ResultEvent(CommandManager.class, ResultType.ERROR, 0, "Unequippable item");
		        }
			}
		}
		return new ResultEvent(CommandManager.class, ResultType.ERROR, 0, "Not an item");
	}
	
	private static ResultEvent execRetreat(Command c, PG player, Enemy enemy){
		int perc = 50;
		int ag1 = 0;
		int ag2 = 0;
		boolean pl = true;
		if(c.getName().equalsIgnoreCase("Player")){
			ag1 = player.getAgility();
			ag2 = enemy.getAttributes().getAgility();
		}else if(c.getName().equalsIgnoreCase("Enemy")){
			ag2 = player.getAgility();
			ag1 = enemy.getAttributes().getAgility();
			pl = false;
		}else
			return new ResultEvent(CommandManager.class, ResultType.ERROR, 0, "invalid command name for a retreat command: must be player or enemy");
		perc = perc+ag1*10 -ag2*10;
		if(perc<10)
			perc = 10;
		else if(perc>100)
			perc = 100;
		Percentage p = new Percentage();
		
		if(p.nextRate(perc)){
			if(pl)
				return new ResultEvent(CommandManager.class, ResultType.RETREAT, 0, null);
			else
				return new ResultEvent(CommandManager.class, ResultType.ENEMY_RETREAT, 0, null);
		}
		if(pl)
			return new ResultEvent(CommandManager.class, ResultType.RETREAT_FAIL, 0, null);
		return new ResultEvent(CommandManager.class, ResultType.ENEMY_RETREAT_FAIL, 0, null);
	}
	
	private static ResultEvent execPlayerAttack(Attack attack, PG player, Enemy enemy){
		Attributes ap = player.getAttributes();
		Attributes ae = enemy.getAttributes();
		int att = ap.getStrength()/2;
		int def = (ae.getStrength()*2 + ae.getDetermination())/3;
		
       if(attack.getAttackType() == Type.MELEE)
           att = ap.getStrength()/2;
       else if(attack.getAttackType() == Type.DISTANCE)
           att = ap.getAgility()/2;
       else if(attack.getAttackType() == Type.MAGIC){
           att = ap.getIntelligence()/2;
           def = (ae.getStrength() + ae.getDetermination())/4; //ignora parte della difesa
       }

		int damage = attack.getDamage() + att - def;
		if (damage < 1)
			damage = 1;
		int succ = attack.getSuccesRate();
		int percentage = succ + ap.getAgility()*succ/50 - ae.getAgility()*succ/50;
		if (percentage < 10)
			percentage = 10;
		else if(percentage>100)
			percentage = 100;
		Percentage perc = new Percentage();
		
		if(perc.nextRate(percentage)){
			int life = enemy.getLife();
			if(damage >= life){
				enemy.setLife(0);
				return new ResultEvent(CommandManager.class, ResultType.WIN, damage, enemy.getName());
			}else{
				enemy.setLife(life - damage);
				return new ResultEvent(CommandManager.class, ResultType.HIT, damage, attack.getName());
			}
		}else
			return new ResultEvent(CommandManager.class, ResultType.MISS, 0, attack.getName());
			
	}
	
	private static ResultEvent execEnemyAttack(Attack attack, PG player, Enemy enemy) {
		Attributes ap = player.getAttributes();
		Attributes ae = enemy.getAttributes();
		int att = ae.getStrength()/2;
		int def = (ap.getStrength()*2 + ap.getDetermination())/3;
		if(player.getEquippedArmor() != null)
			def += player.getEquippedArmor().getDefence();
        if(attack.getAttackType() == Type.MELEE)
            att = ae.getStrength()/2;
        else if(attack.getAttackType() == Type.DISTANCE)
            att = ae.getAgility()/2;
        else if(attack.getAttackType() == Type.MAGIC){
            att = ae.getIntelligence()/2;
            def = (ap.getStrength() + ap.getDetermination())/4; //ignora parte della difesa
            if(player.getEquippedArmor() != null)
    			def += player.getEquippedArmor().getDefence()/3;
        }
		int damage = attack.getDamage() + att - def;
		if (damage < 1)
			damage = 1;
		int succ = attack.getSuccesRate();
		int percentage = succ + ae.getAgility()*succ/50 - ap.getAgility()*succ/50;
		if (percentage < 10)
			percentage = 10;
		else if(percentage>100)
			percentage = 100;
		Percentage perc = new Percentage();
		if(perc.nextRate(percentage)){
			try{
				int life = player.getLife();
				if(damage >= life){
					player.setLife(0);
					return new ResultEvent(CommandManager.class, ResultType.LOSE, damage, null);
				}else{
					player.setLife(life - damage);
					return new ResultEvent(CommandManager.class, ResultType.ENEMY_HIT, damage, attack.getName());
				}
			}catch(Exception e){
				e.printStackTrace();
				return new ResultEvent(CommandManager.class, ResultType.LOSE, damage, null);
			}
		}else
			return new ResultEvent(CommandManager.class, ResultType.ENEMY_MISS, 0, enemy.getName());
	}
	
	private ArrayList<ResultListener> listeners;
	private PG player;
}
